1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 import java.awt.Graphics;
34 import java.util.Stack;
35 import java.awt.event.*;
36 import java.util.ArrayList;
37 import java.util.List;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 @SuppressWarnings("serial")
54 public class CLSFractal
55 extends java.applet.Applet
56 implements Runnable, MouseListener {
57
58 Thread kicker;
59 ContextLSystem cls;
60 int fractLevel = 1;
61 int repaintDelay = 50;
62 boolean incrementalUpdates;
63 float startAngle = 0;
64 float rotAngle = 45;
65 float Xmin;
66 float Xmax;
67 float Ymin;
68 float Ymax;
69 int border;
70 boolean normalizescaling;
71
72 @Override
73 public void init() {
74 String s;
75 cls = new ContextLSystem(this);
76 s = getParameter("level");
77 if (s != null) {
78 fractLevel = Integer.parseInt(s);
79 }
80 s = getParameter("incremental");
81 if (s != null) {
82 incrementalUpdates = s.equalsIgnoreCase("true");
83 }
84 s = getParameter("delay");
85 if (s != null) {
86 repaintDelay = Integer.parseInt(s);
87 }
88 s = getParameter("startAngle");
89 if (s != null) {
90 startAngle = Float.valueOf(s).floatValue();
91 }
92 s = getParameter("rotAngle");
93 if (s != null) {
94 rotAngle = Float.valueOf(s).floatValue();
95 }
96 rotAngle = rotAngle / 360 * 2 * 3.14159265358f;
97 s = getParameter("border");
98 if (s != null) {
99 border = Integer.parseInt(s);
100 }
101 s = getParameter("normalizescale");
102 if (s != null) {
103 normalizescaling = s.equalsIgnoreCase("true");
104 }
105 addMouseListener(this);
106 }
107
108 @Override
109 public void destroy() {
110 removeMouseListener(this);
111 }
112
113 @Override
114 public void run() {
115 Thread me = Thread.currentThread();
116 boolean needsRepaint = false;
117 while (kicker == me && cls.getLevel() < fractLevel) {
118 cls.generate();
119 if (kicker == me && incrementalUpdates) {
120 repaint();
121 try {
122 Thread.sleep(repaintDelay);
123 } catch (InterruptedException ignored) {
124 }
125 } else {
126 needsRepaint = true;
127 }
128 }
129 if (kicker == me) {
130 kicker = null;
131 if (needsRepaint) {
132 repaint();
133 }
134 }
135 }
136
137 @Override
138 public void start() {
139 kicker = new Thread(this);
140 kicker.start();
141 }
142
143 @Override
144 public void stop() {
145 kicker = null;
146 }
147
148
149 @Override
150 public void mouseClicked(MouseEvent e) {
151 }
152
153 @Override
154 public void mousePressed(MouseEvent e) {
155 }
156
157 @Override
158 public void mouseReleased(MouseEvent e) {
159 cls = new ContextLSystem(this);
160 savedPath = null;
161 start();
162 e.consume();
163 }
164
165 @Override
166 public void mouseEntered(MouseEvent e) {
167 }
168
169 @Override
170 public void mouseExited(MouseEvent e) {
171 }
172 String savedPath;
173
174 @Override
175 public void paint(Graphics g) {
176 String fractalPath = cls.getPath();
177 if (fractalPath == null) {
178 super.paint(g);
179 return;
180 }
181 if (savedPath == null || !savedPath.equals(fractalPath)) {
182 savedPath = fractalPath;
183 render(null, fractalPath);
184 }
185
186 for (int i = 0; i < border; i++) {
187 g.draw3DRect(i, i, getSize().width - i * 2, getSize().height - i * 2,
188 false);
189 }
190 render(g, fractalPath);
191 }
192
193 void render(Graphics g, String path) {
194 Stack<CLSTurtle> turtleStack = new Stack<CLSTurtle>();
195 CLSTurtle turtle;
196
197 if (g == null) {
198 Xmin = 1E20f;
199 Ymin = 1E20f;
200 Xmax = -1E20f;
201 Ymax = -1E20f;
202 turtle = new CLSTurtle(startAngle, 0, 0, 0, 0, 1, 1);
203 } else {
204 float frwidth = Xmax - Xmin;
205 if (frwidth == 0) {
206 frwidth = 1;
207 }
208 float frheight = Ymax - Ymin;
209 if (frheight == 0) {
210 frheight = 1;
211 }
212 float xscale = (getSize().width - border * 2 - 1) / frwidth;
213 float yscale = (getSize().height - border * 2 - 1) / frheight;
214 int xoff = border;
215 int yoff = border;
216 if (normalizescaling) {
217 if (xscale < yscale) {
218 yoff += ((getSize().height - border * 2)
219 - ((Ymax - Ymin) * xscale)) / 2;
220 yscale = xscale;
221 } else if (yscale < xscale) {
222 xoff += ((getSize().width - border * 2)
223 - ((Xmax - Xmin) * yscale)) / 2;
224 xscale = yscale;
225 }
226 }
227 turtle = new CLSTurtle(startAngle, 0 - Xmin, 0 - Ymin,
228 xoff, yoff, xscale, yscale);
229 }
230
231 for (int pos = 0; pos < path.length(); pos++) {
232 switch (path.charAt(pos)) {
233 case '+':
234 turtle.rotate(rotAngle);
235 break;
236 case '-':
237 turtle.rotate(-rotAngle);
238 break;
239 case '[':
240 turtleStack.push(turtle);
241 turtle = new CLSTurtle(turtle);
242 break;
243 case ']':
244 turtle = turtleStack.pop();
245 break;
246 case 'f':
247 turtle.jump();
248 break;
249 case 'F':
250 if (g == null) {
251 includePt(turtle.X, turtle.Y);
252 turtle.jump();
253 includePt(turtle.X, turtle.Y);
254 } else {
255 turtle.draw(g);
256 }
257 break;
258 default:
259 break;
260 }
261 }
262 }
263
264 void includePt(float x, float y) {
265 if (x < Xmin) {
266 Xmin = x;
267 }
268 if (x > Xmax) {
269 Xmax = x;
270 }
271 if (y < Ymin) {
272 Ymin = y;
273 }
274 if (y > Ymax) {
275 Ymax = y;
276 }
277 }
278
279 @Override
280 public String getAppletInfo() {
281 return "Title: CLSFractal 1.1f, 27 Mar 1995 \nAuthor: Jim Graham \nA "
282 + "(not yet) Context Sensitive L-System production rule. \n"
283 + "This class encapsulates a production rule for a Context "
284 + "Sensitive\n L-System \n(pred, succ, lContext, rContext)."
285 + " The matches() method, however, does not \n(yet) verify "
286 + "the lContext and rContext parts of the rule.";
287 }
288
289 @Override
290 public String[][] getParameterInfo() {
291 String[][] info = {
292 { "level", "int", "Maximum number of recursions. Default is 1." },
293 { "incremental", "boolean", "Whether or not to repaint between "
294 + "recursions. Default is true." },
295 { "delay", "integer", "Sets delay between repaints. Default is 50." },
296 { "startAngle", "float", "Sets the starting angle. Default is 0." },
297 { "rotAngle", "float", "Sets the rotation angle. Default is 45." },
298 { "border", "integer", "Width of border. Default is 2." },
299 { "normalizeScale", "boolean", "Whether or not to normalize "
300 + "the scaling. Default is true." },
301 { "pred", "String",
302 "Initializes the rules for Context Sensitive L-Systems." },
303 { "succ", "String",
304 "Initializes the rules for Context Sensitive L-Systems." },
305 { "lContext", "String",
306 "Initializes the rules for Context Sensitive L-Systems." },
307 { "rContext", "String",
308 "Initializes the rules for Context Sensitive L-Systems." }
309 };
310 return info;
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323 class CLSTurtle {
324
325 float angle;
326 float X;
327 float Y;
328 float scaleX;
329 float scaleY;
330 int xoff;
331 int yoff;
332
333 public CLSTurtle(float ang, float x, float y,
334 int xorg, int yorg, float sx, float sy) {
335 angle = ang;
336 scaleX = sx;
337 scaleY = sy;
338 X = x * sx;
339 Y = y * sy;
340 xoff = xorg;
341 yoff = yorg;
342 }
343
344 public CLSTurtle(CLSTurtle turtle) {
345 angle = turtle.angle;
346 X = turtle.X;
347 Y = turtle.Y;
348 scaleX = turtle.scaleX;
349 scaleY = turtle.scaleY;
350 xoff = turtle.xoff;
351 yoff = turtle.yoff;
352 }
353
354 public void rotate(float theta) {
355 angle += theta;
356 }
357
358 public void jump() {
359 X += (float) Math.cos(angle) * scaleX;
360 Y += (float) Math.sin(angle) * scaleY;
361 }
362
363 public void draw(Graphics g) {
364 float x = X + (float) Math.cos(angle) * scaleX;
365 float y = Y + (float) Math.sin(angle) * scaleY;
366 g.drawLine((int) X + xoff, (int) Y + yoff,
367 (int) x + xoff, (int) y + yoff);
368 X = x;
369 Y = y;
370 }
371 }
372
373
374
375
376
377
378
379
380
381
382
383
384 class ContextLSystem {
385
386 String axiom;
387 List<CLSRule> rules = new ArrayList<CLSRule>();
388 int level;
389
390 public ContextLSystem(java.applet.Applet app) {
391 axiom = app.getParameter("axiom");
392 int num = 1;
393 while (true) {
394 String pred = app.getParameter("pred" + num);
395 String succ = app.getParameter("succ" + num);
396 if (pred == null || succ == null) {
397 break;
398 }
399 rules.add(new CLSRule(pred, succ,
400 app.getParameter("lContext" + num),
401 app.getParameter("rContext" + num)));
402 num++;
403 }
404 currentPath = new StringBuffer(axiom);
405 level = 0;
406 }
407
408 public int getLevel() {
409 return level;
410 }
411 StringBuffer currentPath;
412
413 public synchronized String getPath() {
414 return ((currentPath == null) ? null : currentPath.toString());
415 }
416
417 private synchronized void setPath(StringBuffer path) {
418 currentPath = path;
419 level++;
420 }
421
422 public void generate() {
423 StringBuffer newPath = new StringBuffer();
424 int pos = 0;
425 while (pos < currentPath.length()) {
426 CLSRule rule = findRule(pos);
427 if (rule == null) {
428 newPath.append(currentPath.charAt(pos));
429 pos++;
430 } else {
431 newPath.append(rule.succ);
432 pos += rule.pred.length();
433 }
434 }
435 setPath(newPath);
436 }
437
438 public CLSRule findRule(int pos) {
439 for (int i = 0; i < rules.size(); i++) {
440 CLSRule rule = rules.get(i);
441 if (rule.matches(currentPath, pos)) {
442 return rule;
443 }
444 }
445 return null;
446 }
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460 class CLSRule {
461
462 String pred;
463 String succ;
464 String lContext;
465 String rContext;
466
467 public CLSRule(String p, String d, String l, String r) {
468 pred = p;
469 succ = d;
470 lContext = l;
471 rContext = r;
472 }
473
474 public boolean matches(StringBuffer sb, int pos) {
475 if (pos + pred.length() > sb.length()) {
476 return false;
477 }
478 char cb[] = new char[pred.length()];
479 sb.getChars(pos, pos + pred.length(), cb, 0);
480 return pred.equals(new String(cb));
481 }
482 }